package com.mixsmart.utils;

import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
 * @author 乌草坡
 * @since 2.5.20
 */
public class ImageUtils {

    private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class);

    private static final Integer ZERO = 0;

    private static final Integer ONE_ZERO_TWO_FOUR = 1024;

    private static final Integer NINE_ZERO_ZERO = 900;

    private static final Integer THREE_TWO_SEVEN_FIVE = 3275;

    private static final Integer TWO_ZERO_FOUR_SEVEN = 2047;

    private static final Double ZERO_EIGHT_FIVE = 0.85;

    private static final Double ZERO_SIX = 0.6;

    private static final Double ZERO_FOUR_FOUR = 0.44;

    private static final Double ZERO_FOUR = 0.4;

    /**
     * 按大小压缩图片大小
     * @param filePath 图片路径
     * @param destFileSize 需要压缩到的大小；单位: KB
     */
    public static void compressImageBySize(String filePath, long destFileSize) {
        if(StringUtils.isEmpty(filePath)) {
            return;
        }
        compressImageBySize(filePath, destFileSize, null);
    }

    /**
     * 按大小压缩图片大小
     * @param filePath 图片路径
     * @param destFileSize 需要压缩到的大小；单位: KB
     * @param destFilePath 压缩后的文件；如果不传，则会覆盖原来的文件
     */
    public static void compressImageBySize(String filePath, long destFileSize, String destFilePath) {
        if(StringUtils.isEmpty(filePath)) {
            return;
        }
        compressImageBySize(new File(filePath), destFileSize, destFilePath);
    }

    /**
     * 按大小压缩图片大小
     * @param file 图片文件
     * @param destFileSize 需要压缩到的大小；单位: KB
     * @param destFilePath 压缩后的文件；如果不传，则会覆盖原来的文件
     */
    public static void compressImageBySize(File file, long destFileSize, String destFilePath) {
        File destFile = null;
        if(StringUtils.isNotEmpty(destFilePath)) {
            destFile = new File(destFilePath);
        }
        compressImageToDestFileBySize(file, destFileSize, destFile);
    }

    /**
     * 按大小压缩图片
     * @param file 图片文件
     * @param destFileSize 需要压缩的大小；单位：KB
     */
    public static void compressImageToDestFileBySize(File file, long destFileSize, File destFile) {
        if(null == file || !file.exists() || !file.isFile() || destFileSize <= ZERO) {
            return;
        }
        long sourceSize = file.length();
        LoggerUtils.debug(logger,"原图片大小为：{} KB,目标图片压缩的大小为:{} KB",sourceSize / ONE_ZERO_TWO_FOUR, destFileSize);
        if(destFileSize * ONE_ZERO_TWO_FOUR >= sourceSize) {
            LoggerUtils.debug(logger,"原图片小于等于期望的目标文件的大小，不需要压缩处理");
            return;
        }
        try {
            if(null != destFile && !destFile.exists()) {
                destFile.createNewFile();
            }
            byte[] bytes = IOUtils.toByteArray(new FileInputStream(file));
            byte[] newBytes = compressPicForScale(bytes, destFileSize);
            FileOutputStream outputStream = null;
            if(null != destFile) {
                outputStream = new FileOutputStream(destFile);
            } else {
                outputStream = new FileOutputStream(file);
            }
            IOUtils.write(newBytes, outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 按大小压缩图片
     * @param file 图片文件
     * @param destFileSize 需要压缩的大小；单位：KB
     */
    public static void compressImageBySize(File file, long destFileSize) {
        compressImageToDestFileBySize(file, destFileSize, null);
    }

    /**
     * 根据指定大小压缩图片
     *
     * @param imageBytes  源图片字节数组
     * @param destSize 指定图片大小，单位kb
     * @return 压缩质量后的图片字节数组
     */
    private static byte[] compressPicForScale(byte[] imageBytes, long destSize) {
        long destFileSize = destSize * ONE_ZERO_TWO_FOUR;
        long byteLen = imageBytes.length;
        if (byteLen <= ZERO || byteLen <= destFileSize) {
            return imageBytes;
        }
        long srcSize = imageBytes.length;
        double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR);
        LoggerUtils.debug(logger, "原图片大小为:{}, 目标压缩大小为:{}", srcSize, destFileSize);
        try {
            int index = 1;
            while (imageBytes.length > destFileSize) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
                Thumbnails.of(inputStream)
                        .scale(accuracy)
                        .outputQuality(accuracy)
                        .toOutputStream(outputStream);
                imageBytes = outputStream.toByteArray();
                LoggerUtils.debug(logger,"{}次压缩后大小={}kb",index, imageBytes.length / ONE_ZERO_TWO_FOUR);
                index++;
            }
            LoggerUtils.debug(logger,"图片原大小={}kb | 压缩后大小={}kb",
                    srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR);
        } catch (Exception e) {
            LoggerUtils.error(logger, "【图片压缩】msg=图片压缩失败!", e);
        }
        return imageBytes;
    }

    /**
     * 自动调节精度(经验数值)
     * @param size 源图片大小
     * @return 图片压缩质量比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < NINE_ZERO_ZERO) {
            accuracy = ZERO_EIGHT_FIVE;
        } else if (size < TWO_ZERO_FOUR_SEVEN) {
            accuracy = ZERO_SIX;
        } else if (size < THREE_TWO_SEVEN_FIVE) {
            accuracy = ZERO_FOUR_FOUR;
        } else {
            accuracy = ZERO_SIX;
        }
        LoggerUtils.debug(logger, "压缩率为:{}", accuracy);
        return accuracy;
    }
}
